package com.cloud.easy.im.utils;

import java.util.concurrent.atomic.AtomicInteger;

import static java.util.Objects.hash;

/**
 * 消息中心消息id生成器(80Bit)
 * | ------------- |   | ------------- |   | ------------- |   | ------------- |
 * | 时间戳（42Bit）| + | 自旋ID（12Bit）| + | 会话类型（4Bit）| + | 会话ID（22it）|
 * | ------------- |   | ------------- |   | ------------- |   | ------------- |
 * <p>
 * 消息 ID 共占 80 Bit ，计算时我们分为两部分，高 60 Bit （记为 highBits）和低 20 Bit （记为 lowBits）
 * 如：5TW9-F4FV-003T-2KQU
 * |-----高位----|低位|
 *
 * @author wangjian
 * @version 1.0.0
 * @create 2020年11月17日 11:13
 * @since JDK 1.8
 * Life is so short,do something to make yourself happy,such as coding
 */
public class SequenceIdutil {

    /**
     * 最大16进制标识
     */
    private final static int MAX_MESSAGE_SEQ = 0xFFF;

    /**
     * 自选ID
     */
    private static AtomicInteger currentSeq = new AtomicInteger(0);

    private static final Object lock = new Object();

    /**
     * 32个字符，用来表示32进制,去掉了难以区分的 O,I,S
     */
    final static char[] DIGITS = {
            '0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'A', 'B',
            'C', 'D', 'E', 'F', 'G', 'H',
            'J', 'K', 'L', 'M', 'N', 'P',
            'Q', 'R', 'T', 'U', 'V', 'W',
            'X', 'Y'};

    /**
     * 获取自选id
     *
     * @return
     */
    private static int getMessageSeq() {
        int ret = currentSeq.addAndGet(1);
        if (ret > MAX_MESSAGE_SEQ) {
            synchronized (lock) {
                if (ret > MAX_MESSAGE_SEQ) {
                    currentSeq.set(0);
                    ret = currentSeq.addAndGet(1);
                }
            }
        }
        return ret;
    }

    /**
     * 获取消息ID
     * 消息 ID 共占 80 Bit ，计算时我们分为两部分，高 60 Bit （记为 highBits）和低 20 Bit （记为 lowBits）
     * 如：5TW9-F4FV-003T-2KQU
     * |-------高位------|低位|
     *
     * @param utype     会话类型
     * @param sessionId 消息会话ID
     * @return
     */
    public static String getMsgId(byte utype, String sessionId) {
        //获取当前时间
        long hightBits = SystemClock.getInstance().getNow();
        hightBits = hightBits << 12;
        // 高位拼接自旋ID
        hightBits = hightBits | getMessageSeq();
        // 让出右4位存放会话类型
        hightBits = hightBits << 4;
        // 将4位16进制的类型表示设置到高位
        hightBits = hightBits | (utype & 0xF);
        // 获取低22位
        int sessionIdInt = hash(sessionId) & 0x3FFFFF;
        // highBits 左移 2 位，并将 sessionIdInt 的高 2 位拼接到 highBits 的低 2 位中
        hightBits = hightBits << 2;
        hightBits = hightBits | (sessionIdInt >> 20);
        //取会话 ID 的低 16 位作为 lowBits
        int lowBits = sessionIdInt & 0xFFFFF;
        return digits32(hightBits) + "-" + digits32(lowBits);
    }

    /**
     * long类型转为32进制，指定了使用的字符，参考Long.toUnsignedString0
     *
     * @param val
     * @return
     */
    static String digits32(long val) {
        // 32=2^5=二进制100000
        int shift = 5;
        // numberOfLeadingZeros 获取long值从高位连续为0的个数，比如val=0，则返回64
        // 此处mag=long值二进制减去高位0之后的长度
        int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
        int len = Math.max(((mag + (shift - 1)) / shift), 1);
        if (len != 4) {
            len += 2;
        }
        char[] buf = new char[len];
        int num = 0;
        do {
            if (num == 4 || num == 8) {
                buf[--len] = '-';
            }
            // &31相当于%32
            buf[--len] = DIGITS[((int) val) & 31];
            num++;
            val >>>= shift;
        } while (val != 0 && len > 0);
        return new String(buf);
    }
}
